package myAsm;

import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.AbstractInsnNode;



@Entity
@Table(name="INSN_INSTRUCTIONS")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="INSN",discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("INSN_ABSTRACT")
public abstract class MyAbstractInsnNode {
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="INSN_ID")
	public Integer id;
	
    /**
     * The type of {@link InsnNode} instructions.
     */
	@Transient
    public static final int INSN = 0;

    /**
     * The type of {@link IntInsnNode} instructions.
     */
	@Transient
    public static final int INT_INSN = 1;

    /**
     * The type of {@link VarInsnNode} instructions.
     */
	@Transient
    public static final int VAR_INSN = 2;

    /**
     * The type of {@link TypeInsnNode} instructions.
     */
	@Transient
    public static final int TYPE_INSN = 3;

    /**
     * The type of {@link FieldInsnNode} instructions.
     */
	@Transient
    public static final int FIELD_INSN = 4;

    /**
     * The type of {@link MethodInsnNode} instructions.
     */
	@Transient
    public static final int METHOD_INSN = 5;

    /**
     * The type of {@link JumpInsnNode} instructions.
     */
	@Transient
    public static final int JUMP_INSN = 6;

    /**
     * The type of {@link LabelNode} "instructions".
     */
	@Transient
    public static final int LABEL = 7;

    /**
     * The type of {@link LdcInsnNode} instructions.
     */
	@Transient
    public static final int LDC_INSN = 8;

    /**
     * The type of {@link IincInsnNode} instructions.
     */
	@Transient
    public static final int IINC_INSN = 9;

    /**
     * The type of {@link TableSwitchInsnNode} instructions.
     */
	@Transient
    public static final int TABLESWITCH_INSN = 10;

    /**
     * The type of {@link LookupSwitchInsnNode} instructions.
     */
	@Transient
    public static final int LOOKUPSWITCH_INSN = 11;

    /**
     * The type of {@link MultiANewArrayInsnNode} instructions.
     */
	@Transient
    public static final int MULTIANEWARRAY_INSN = 12;

    /**
     * The type of {@link FrameNode} "instructions".
     */
	@Transient
    public static final int FRAME = 13;

    /**
     * The type of {@link LineNumberNode} "instructions".
     */
	@Transient
    public static final int LINE = 14;

    /**
     * The opcode of this instruction.
     */
	@Column(name="INSN_OPCODE")
    protected int opcode;

	/**
     * Index of this instruction in the method instruction list
     */
    @Column(name="INSN_INDEX")
    private int index;
    
    /**
     * Previous instruction in the list to which this instruction belongs.
     */
    @Transient
    MyAbstractInsnNode prev;

    /**
     * Next instruction in the list to which this instruction belongs.
     */
    @Transient
    MyAbstractInsnNode next;

    /**
     * Constructs a new {@link AbstractInsnNode}.
     * 
     * @param opcode the opcode of the instruction to be constructed.
     */
    public MyAbstractInsnNode(final int opcode) {
        this.opcode = opcode;
        this.setIndex(-1);
    }
    
    /**
     * Returns the opcode of this instruction.
     * 
     * @return the opcode of this instruction.
     */
    public int getOpcode() {
        return opcode;
    }

    /**
     * Returns the type of this instruction.
     * 
     * @return the type of this instruction, i.e. one the constants defined in
     *         this class.
     */
    public abstract int getType();

    /**
     * Returns the next instruction in the list to which this instruction
     * belongs, if any.
     * 
     * @return the next instruction in the list to which this instruction
     *         belongs, if any. May be <tt>null</tt>.
     */
    public MyAbstractInsnNode getNext() {
        return next;
    }
    
    
    public MyAbstractInsnNode getPrevious() {
        return prev;
    }
    
    /**
     * Makes the given code visitor visit this instruction.
     * 
     * @param cv a code visitor.
     */
    public abstract void accept(final MethodVisitor cv);

    /**
     * Returns a copy of this instruction.
     * 
     * @param labels a map from LabelNodes to cloned LabelNodes.
     * @return a copy of this instruction. The returned instruction does not
     *         belong to any {@link InsnList}.
     */
    public abstract MyAbstractInsnNode clone(final Map labels);

    /**
     * Returns the clone of the given label.
     * 
     * @param label a label.
     * @param map a map from LabelNodes to cloned LabelNodes.
     * @return the clone of the given label.
     */
    static MyLabelNode clone(final MyLabelNode label, final Map map) {
        return (MyLabelNode) map.get(label);
    }

    /**
     * Returns the clones of the given labels.
     * 
     * @param labels a list of labels.
     * @param map a map from LabelNodes to cloned LabelNodes.
     * @return the clones of the given labels.
     */
    static MyLabelNode[] clone(final List labels, final Map map) {
        MyLabelNode[] clones = new MyLabelNode[labels.size()];
        for (int i = 0; i < clones.length; ++i) {
            clones[i] = (MyLabelNode) map.get(labels.get(i));
        }
        return clones;
    }

	public void setIndex(int index) {
		this.index = index;
	}

	public int getIndex() {
		return index;
	}
    
}
